import { Logger } from '@nestjs/common';
import { Scraper } from '../scraper';
import { EmptyGoods, IGoods, IPrice, IScraperProp, IShop } from '../types';
import { ElementHandle, Page } from 'puppeteer';
import { scrapeSleep, shuffleArray } from '../utils/util';
// 最大爬的页数
const MAX_PAGE = 3;

// 京东特有的选择器配置
const SELECTORS = {
  inputText: 'input.input_text',
  inputSubmit: 'input.input_submit',
  nextPage: 'a.pn-next',
  nextPageDisable: 'a.pn-next.disabled',
  shops: '#J_goodsList div.gl-i-wrap',
  goodsList: 'div#choose-attr-1 div.item',
  goodsPrice: 'div.displayPrice--oD54EJg0 span.text--fZ9NUhyQ',
  shopName: '.p-shop',
  shopUrl: 'a',
  goodsName: 'valueItem--GzWd2LsV isSelected--YrA6x4Yj',
  choosedGoods: 'div#choose-attr-1 div.item.selected',
} as const;

export class JDScraper extends Scraper {
  cookie: string | undefined;
  taskId: number;
  constructor({ url, keyword, ...props }: IScraperProp) {
    super({ url, keyword, ...props });
    this.taskId = Number(props.taskId);
  }

  async scraping() {
    if (isNaN(this.taskId)) {
      Logger.error(`Scraping Task ID is invalid!`);
      return;
    }
    // 初始化浏览器
    const page: Page = await this.launchBrowser(false);
    // 搜索结果
    const searchPage = await this.search(page);
    // 爬取搜索结果页
    await this.scrapeSearchResultPage(searchPage);
  }

  // 打开搜索页面并输入关键词
  private async search(page: Page): Promise<Page> {
    try {
      Logger.log(`goto ${this.url}`);
      await page.goto(this.url, { waitUntil: 'networkidle2' });
      await scrapeSleep();
      await page.type(SELECTORS.inputText, this.keyword);
      await scrapeSleep();
      const result = await Promise.all([
        page.waitForNavigation({ waitUntil: 'load' }),
        page.click(SELECTORS.inputSubmit),
      ]);
      Logger.log('input submit result', result);
      Logger.log(`entry the search page ${page.url()}`);
      //await page.click('input.input_submit');
      await scrapeSleep();
      return page;
    } catch (err) {
      Logger.error(`打开搜索页面失败: ${err}`);
      throw err;
    }
  }

  // 检查是否存在下一页（带重试）
  private async hasNextPage(page: Page, retries = 3): Promise<boolean> {
    let attempt = 0;

    while (attempt < retries) {
      try {
        const nextButton = await page.$(SELECTORS.nextPage);
        if (!nextButton) return false;

        const isDisabled = await page.$(SELECTORS.nextPageDisable);
        return !isDisabled;
      } catch (err) {
        Logger.error(`检查下一页失败，第 ${attempt + 1} 次重试: ${err}`);
        await page.reload();
        await scrapeSleep(20000, 30000);

        attempt++;
        if (attempt === retries) {
          Logger.error(`重试 ${retries} 次后失败`);
          return false;
        }
      }
    }
    return false;
  }

  private async scrapeSearchResultPage(page: Page) {
    const scraped = new Set<string>();
    let pageCount = 1;
    while (true) {
      await this.monitorHumanScroll(page);
      await this.delayRandomly(2000, 5000);
      await this.mouseRandomly(page);
      await this.scrap(page, scraped);
      pageCount++;
      if (pageCount > MAX_PAGE) break;
      const hasNext = await this.hasNextPage(page, 3);
      if (!hasNext) {
        Logger.log('已到达最后一页');
        break;
      }

      await Promise.all([
        page.waitForNavigation({ waitUntil: 'load' }),
        page.click(SELECTORS.nextPage),
      ]);
      Logger.log(`跳转到搜索结果下一页 ${this.taskId}`);
      await this.delayRandomly(3000, 7000);
      Logger.log(`跳转到搜索结果下一页 ${this.taskId}`);
    }
    Logger.log(`finished task scraping ${this.taskId}`);
  }

  async scrap(page: Page, scraped: Set<string>) {
    // wait page
    if (!page) {
      Logger.error('current page not initialiazed');
      return;
    }
    Logger.log(`scraping shops ${page.url()}`);
    const shops = await page?.$$(SELECTORS.shops);
    if (!shops) {
      Logger.error(`None of shop found ${page.url()}`);
      return;
    }
    Logger.log(`${shops.length} items has found`);
    for (const shop of shops) {
      const jup = await shop.$(SELECTORS.shopUrl);
      if (!jup) {
        Logger.error(`can not found <a> ${page.url()}`);
        continue;
      }
      // 打开商品页
      const newpage = await this.clickOpenGoodsPage(
        jup,
        'https://search.jd.com/',
      );
      Logger.log(`跳转到商品详情页 ${newpage.url()}`);

      const scrapedRet = await this.scrapGoods(newpage);
      // write to the database;
      await this.store(scrapedRet);
      await newpage.close();
      await this.delayRandomly();
    }
  }
  async getPrices(page: Page): Promise<IPrice | null> {
    if (!page) {
      Logger.error('商品详情页没有加载正确');
      return null;
    }
    const prices = await page.$$eval(
      'div.summary-price-wrap span.price',
      (el) => el.map((e) => e.textContent).filter((p) => p != ''),
    );
    Logger.log(`scraped prices: ${prices}`);
    if (!prices) {
      return null;
    }
    const resultPrices = prices?.filter((p) => p != '');
    if (resultPrices?.length == 1) {
      const price = Number(resultPrices[0]?.trim());
      return { shop_price: price || 0, platform_price: price || 0 };
    } else {
      return {
        shop_price: Number(resultPrices[0]?.trim()),
        platform_price: Number(resultPrices[1]?.trim()),
      };
    }
  }

  async getFilteredSkuIds(page: Page): Promise<string[]> {
    // 获取商品详情页面所有的商品sku id
    const skuId = await page.$$eval(
      'div#choose-attr-1 div.item',
      (elements, key_words) => {
        const el = elements.filter((el) =>
          el.getAttribute('data-value')?.includes(key_words),
        );
        return el.map((el) => el.getAttribute('data-sku'));
      },
      this.keyword,
    );
    // 过滤掉可能为 null 的值，确保 spus 数组中的每个元素都是 string 类型
    const validSkuIds = skuId.filter((spu): spu is string => spu !== null);
    return validSkuIds;
  }

  async scrapGoods(page: Page): Promise<IGoods[]> {
    const result: IGoods[] = [];
    if (!page) {
      Logger.error('current page not setted');
      return result;
    }
    await this.delayRandomly();
    const skuIdList = await this.getFilteredSkuIds(page);
    for (const skuId of skuIdList) {
      await this.delayRandomly(3000, 8000);
      const goods = await this.scrapeGoodsDetails(page, skuId);
      await this.mouseRandomly(page);
      await this.delayRandomly(3000, 8000);
      if (!goods) {
        continue;
      }
      result.push(goods);
    }
    return result;
  }
  async scrapeGoodsDetails(page: Page, skuId: string): Promise<IGoods | null> {
    const goods: { [key: string]: any } = {};
    goods['source'] = 'JD';
    const subUrl = `https://item.jd.com/${skuId}.html#none`;
    Logger.debug(`获取具体商品型号和价格详情：${subUrl}`);
    const newPage = await this.browser?.newPage();
    if (!newPage) {
      return null;
    }
    this.initNewTab(newPage, 'https://search.jd.com/');
    await newPage.goto(subUrl, { waitUntil: 'networkidle2' });
    await this.delayRandomly(10000, 15000);
    await this.simulateHumanScroll(newPage, 500, 1000);
    await this.mouseRandomly(newPage);
    await this.delayRandomly(1000, 2000);
    const selected = `div.item[data-sku="${skuId}"]`;
    await this.scrollToSelector(newPage, selected);
    try {
      const sku_name = await newPage.$eval(
        SELECTORS.choosedGoods,
        (el) => el.innerText,
      );
      const prices = await this.getPrices(newPage);
      const shopName = await newPage.$eval('div#summary-service a', (el) =>
        el.innerText.trim(),
      );
      Logger.debug(
        `${shopName}: ${sku_name}(${skuId}): 平台价格-${prices?.platform_price} 最终价格-${prices?.shop_price}`,
      );

      const info = await newPage.$eval('div.p-parameter', (el) => el.innerText);
      const goods: IGoods = {
        taskId: this.taskId,
        skuName: sku_name,
        skuId: skuId,
        shopName: shopName,
        shopPrice: prices?.shop_price || 0,
        platformPrice: prices?.platform_price || 0,
        source: 'jd',
        detail: info || '',
        updateTime: new Date(),
      };
      return goods;
    } catch (err) {
      Logger.error(`解析网页selector出错，url=${subUrl},error=${err}`);
    } finally {
      newPage.close();
    }
    return null;
  }
}
